Introduction
The Elite: Dangerous Database (EDDB) is a collection of data for the game Elite: Dangerous. The data is split into several different databases:
- Prices of goods (listings.csv)
- Stations (stations.json)
- Populated Systems (systems_populated.json)
- Factions (factions,json)
- Commodities (commodities.json)
- Modules (modules.json)
This notebook will be an introduction to each data set and provide some exploratory data analysis of the data sets. This EDA will also act as prototypes for the information I will want to display in the Shiny app.
library(tidyverse)
library(jsonlite)
library(plotly)
Prices
The listings.csv file gives a read out of the prices of goods at the time the file was updated.
listings <- read_csv("data/listings.csv", progress = FALSE)
listings
The columns for the listings are:
- id: The id of the particular good
- station_id: The station where the good is being sold
- commodity_id: The good being sold (related to commodities.json)
- supply: How much of the item is available for purchase
- supply_bracket: Unsure of purpose
- buy_price: Price the good can be bought at
- sell_price: Price the good can be sold at
- demand: How many items the station wants
- demand_bracket: Unsure of purpose
- collected_at: Time the data was collected
We can then look at a summary of the data to get an idea of what is in the data set.
summary(listings)
id station_id commodity_id supply
Min. : 1 Min. : 1 Min. : 1.00 Min. : 0
1st Qu.:1187777 1st Qu.:13470 1st Qu.: 22.00 1st Qu.: 0
Median :2338836 Median :27759 Median : 49.00 Median : 0
Mean :2615026 Mean :29000 Mean : 87.71 Mean : 27006
3rd Qu.:3698904 3rd Qu.:42522 3rd Qu.:103.00 3rd Qu.: 31
Max. :6900247 Max. :69881 Max. :330.00 Max. :23917110
supply_bracket buy_price sell_price demand
Min. :0.00 Min. : 0.0 Min. : 0 Min. : 0
1st Qu.:0.00 1st Qu.: 0.0 1st Qu.: 547 1st Qu.: 0
Median :0.00 Median : 0.0 Median : 1357 Median : 664
Mean :0.56 Mean : 535.2 Mean : 5247 Mean : 116685
3rd Qu.:1.00 3rd Qu.: 63.0 3rd Qu.: 3980 3rd Qu.: 12064
Max. :3.00 Max. :113355.0 Max. :257990 Max. :583601700
NA's :218269
demand_bracket collected_at
Min. :0.00 Min. :1.454e+09
1st Qu.:0.00 1st Qu.:1.523e+09
Median :2.00 Median :1.527e+09
Mean :1.81 Mean :1.523e+09
3rd Qu.:3.00 3rd Qu.:1.528e+09
Max. :3.00 Max. :1.529e+09
NA's :218269
From the summary, we can see that there are number of NA’s in the supply_bracket and demand_backet columns. Since we don’t know what those do, we can ignore those for the time being. Additionally, there are zeroes in the sell_price and buy_price columns. Those are essentially NA’s as a sell_price of 0 means that you can’t sell the product there and a buy_price of 0 means there are none available at that station.
listings %>%
filter(commodity_id == 1) %>%
select(buy_price, sell_price) %>%
gather(type, price) %>%
filter(price > 0) %>%
group_by(type) %>%
mutate(average = mean(price), median = median(price)) %>%
ggplot(aes(x = price, fill = type)) +
geom_histogram(bins = 100) +
facet_grid(type ~.) +
geom_vline(aes(xintercept = average)) +
geom_vline(aes(xintercept = median), linetype = "dotted")

This figure gives a quick histogram of the buy and sell prices of a single commodity across all available stations. Note that it filters out any buy or sell prices of 0. The solid line is the mean of the distribution and the dotted line is the median of the distribution. From the figure, an interesting topic to investigate is looking at comparative buy and sell prices across stations to create a system to search for the max difference.
listings %>%
filter(station_id == 12) %>%
select(buy_price, sell_price) %>%
gather(type, price) %>%
filter(price > 0) %>%
group_by(type) %>%
mutate(average = mean(price), median = median(price)) %>%
ggplot(aes(x = price, fill = type)) +
geom_histogram(bins = 20) +
facet_grid(type ~.) +
geom_vline(aes(xintercept = average)) +
geom_vline(aes(xintercept = median), linetype = "dotted")

This figure looks at a histogram of buy and sell prices for a specific station (across all available commodities). Again, the solid line is the mean and the dotted line is the median.
Questions
I want to put together some initial questions to answer using my Shiny app.
- What is the biggest price difference?
- What is the distance between the stations with the biggest price difference?
- Given a particular commodity, where can I find it and at what price?
- Given a particular commodity, where can I sell it and at what price?
- How do the buying and selling price compare the the galatic average (and reported average average and median)?
Stations
stations <- fromJSON("data/stations.json")
Error in fromJSON("data/stations.json") :
could not find function "fromJSON"
The stations data has 39 columns in it, the most relevant of which are the following:
- id: The station’s id
- name: The station’s name
- system_id: The system id where the station resides
- updated_at: Time at which the data was updated
- distance_to_star: Distance of the station from the system’s star
- has_ : A set of booleans showing what amenities the station offers
- is_planetary: A boolean showing whether the station is on a planet
- selling_ships: A list of the ships being sold
There are also a number of other useful variables showing additional information about the stations.
We can summarize the most relevant columns and get a sense of distributions and any possible NAs.
stations %>%
select(id, system_id, updated_at, distance_to_star, starts_with("has_"), is_planetary) %>%
summary()
id system_id updated_at distance_to_star
Min. : 1 Min. : 1 Min. :1.479e+09 Min. : 2
1st Qu.:18114 1st Qu.: 4843 1st Qu.:1.527e+09 1st Qu.: 200
Median :35262 Median : 10419 Median :1.528e+09 Median : 868
Mean :35128 Mean : 62959 Mean :1.527e+09 Mean : 17005
3rd Qu.:52285 3rd Qu.: 15469 3rd Qu.:1.529e+09 3rd Qu.: 2670
Max. :69883 Max. :17805395 Max. :1.529e+09 Max. :6783706
NA's :1951
has_blackmarket has_market has_refuel has_repair
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:46586 FALSE:14485 FALSE:7157 FALSE:13333
TRUE :21261 TRUE :53362 TRUE :60690 TRUE :54514
has_rearm has_outfitting has_shipyard has_docking
Mode :logical Mode :logical Mode :logical Mode :logical
FALSE:21408 FALSE:23268 FALSE:51848 FALSE:5548
TRUE :46439 TRUE :44579 TRUE :15999 TRUE :62299
has_commodities is_planetary
Mode :logical Mode :logical
FALSE:16943 FALSE:40952
TRUE :50904 TRUE :26895
We can also extract a list of possible ships that are sold and from that we can determine what stations sell which ships.
stations %>%
select(selling_ships) %>%
unnest() %>%
distinct()
stations %>%
select(id, name, system_id, selling_ships) %>%
unnest() %>%
filter(selling_ships == "Imperial Cutter") %>%
head()
For instance, we can get a listing of all of the stations that sell the “Imperial Cutter” and what system that station is in.
Questions
- What are the list of purchasable ships?
- What ship can be bought where?
- What module can be bought where?
Populated Systems
The populated systems data gives information on systems in the universe that are populated.
populated_systems <- as_tibble(fromJSON("data/systems_populated.json"))
head(populated_systems)
Like the stations data, the populated systems data has a large number of columns (29) that are mostly metadata about the systems. Most of the metadata is centered on government types, alleigance, security level, power play info, and factions. The information that will likely be useful for us are the following columns:
- id: The system’s id
- name: the system’s name
- x, y, z: The coordinates of the system in the universe
First, I want to look at the summary statistics for the coordinates to verify that there are no missing coordinates. It looks like all of the coordinates are present.
populated_systems %>%
select(x, y, z) %>%
summary()
x y z
Min. :-9557.94 Min. :-944.12 Min. :-6947.56
1st Qu.: -49.50 1st Qu.: -99.69 1st Qu.: -47.94
Median : 17.23 Median : -25.05 Median : 14.75
Mean : -20.58 Mean : -35.68 Mean : 84.76
3rd Qu.: 83.07 3rd Qu.: 34.25 3rd Qu.: 74.38
Max. : 2704.97 Max. : 366.66 Max. :19853.19
I can make a 3D scatter plot of the coordinates of the systems and show the color of the controlling power. All of this done with plotly and could be a fun way of showing where a particular system is in comparison to other systems. In this plot I’ve only included the top 4 controlling powers (based on number of systems controlled). Since I haven’t used plotly much, this could be a great opportunity to become more familiar with it.
p <- populated_systems %>%
filter(!is.na(power)) %>%
group_by(power) %>%
mutate(control_number = n()) %>%
ungroup() %>%
mutate(control_number = dense_rank(desc(control_number))) %>%
filter(control_number < 5) %>%
plot_ly(x = ~x, y = ~y, z = ~z, color = ~power) %>%
add_markers(opacity = 0.1)
p
Questions
- Where are the various systems?
- What powers or factions control each system?
Factions
The factions data set gives metadata on all of the factions involved in the game.
factions <- as_tibble(fromJSON("data/factions.json"))
head(factions)
I am unlikely to use this data set for this particular project, but it could provide additional information if I want to look at specific factions, particularly player created factions.
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiBhbmQgRURBIGZvciBFRERCIg0KYXV0aG9yOiAiQnJpYW4gUmljaGFyZHMiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCiMjIEludHJvZHVjdGlvbg0KDQpUaGUgRWxpdGU6IERhbmdlcm91cyBEYXRhYmFzZSAoRUREQikgaXMgYSBjb2xsZWN0aW9uIG9mIGRhdGEgZm9yIHRoZSBnYW1lIA0KRWxpdGU6IERhbmdlcm91cy4gVGhlIGRhdGEgaXMgc3BsaXQgaW50byBzZXZlcmFsIGRpZmZlcmVudCBkYXRhYmFzZXM6IA0KDQoqIFByaWNlcyBvZiBnb29kcyAobGlzdGluZ3MuY3N2KQ0KKiBTdGF0aW9ucyAoc3RhdGlvbnMuanNvbikNCiogUG9wdWxhdGVkIFN5c3RlbXMgKHN5c3RlbXNfcG9wdWxhdGVkLmpzb24pDQoqIEZhY3Rpb25zIChmYWN0aW9ucyxqc29uKQ0KKiBDb21tb2RpdGllcyAoY29tbW9kaXRpZXMuanNvbikNCiogTW9kdWxlcyAobW9kdWxlcy5qc29uKQ0KDQpUaGlzIG5vdGVib29rIHdpbGwgYmUgYW4gaW50cm9kdWN0aW9uIHRvIGVhY2ggZGF0YSBzZXQgYW5kIHByb3ZpZGUgc29tZSANCmV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgb2YgdGhlIGRhdGEgc2V0cy4gVGhpcyBFREEgd2lsbCBhbHNvIGFjdCBhcyANCnByb3RvdHlwZXMgZm9yIHRoZSBpbmZvcm1hdGlvbiBJIHdpbGwgd2FudCB0byBkaXNwbGF5IGluIHRoZSBTaGlueSBhcHAuDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoanNvbmxpdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmBgYA0KDQojIyBQcmljZXMNCg0KVGhlIGBsaXN0aW5ncy5jc3ZgIGZpbGUgZ2l2ZXMgYSByZWFkIG91dCBvZiB0aGUgcHJpY2VzIG9mIGdvb2RzIGF0IHRoZSB0aW1lIA0KdGhlIGZpbGUgd2FzIHVwZGF0ZWQuDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9DQpsaXN0aW5ncyA8LSByZWFkX2NzdigiZGF0YS9saXN0aW5ncy5jc3YiLCBwcm9ncmVzcyA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChsaXN0aW5ncykNCmBgYA0KDQpUaGUgY29sdW1ucyBmb3IgdGhlIGBsaXN0aW5nc2AgYXJlOg0KDQoqIGlkOiBUaGUgaWQgb2YgdGhlIHBhcnRpY3VsYXIgZ29vZA0KKiBzdGF0aW9uX2lkOiBUaGUgc3RhdGlvbiB3aGVyZSB0aGUgZ29vZCBpcyBiZWluZyBzb2xkDQoqIGNvbW1vZGl0eV9pZDogVGhlIGdvb2QgYmVpbmcgc29sZCAocmVsYXRlZCB0byBjb21tb2RpdGllcy5qc29uKQ0KKiBzdXBwbHk6IEhvdyBtdWNoIG9mIHRoZSBpdGVtIGlzIGF2YWlsYWJsZSBmb3IgcHVyY2hhc2UNCiogc3VwcGx5X2JyYWNrZXQ6IFVuc3VyZSBvZiBwdXJwb3NlDQoqIGJ1eV9wcmljZTogUHJpY2UgdGhlIGdvb2QgY2FuIGJlIGJvdWdodCBhdCANCiogc2VsbF9wcmljZTogUHJpY2UgdGhlIGdvb2QgY2FuIGJlIHNvbGQgYXQNCiogZGVtYW5kOiBIb3cgbWFueSBpdGVtcyB0aGUgc3RhdGlvbiB3YW50cw0KKiBkZW1hbmRfYnJhY2tldDogVW5zdXJlIG9mIHB1cnBvc2UNCiogY29sbGVjdGVkX2F0OiBUaW1lIHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQNCg0KV2UgY2FuIHRoZW4gbG9vayBhdCBhIHN1bW1hcnkgb2YgdGhlIGRhdGEgdG8gZ2V0IGFuIGlkZWEgb2Ygd2hhdCBpcyBpbiB0aGUgDQpkYXRhIHNldC4NCg0KYGBge3J9DQpzdW1tYXJ5KGxpc3RpbmdzKQ0KYGBgDQoNCkZyb20gdGhlIHN1bW1hcnksIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgbnVtYmVyIG9mIE5BJ3MgaW4gdGhlIGBzdXBwbHlfYnJhY2tldGAgDQphbmQgYGRlbWFuZF9iYWNrZXRgIGNvbHVtbnMuIFNpbmNlIHdlIGRvbid0IGtub3cgd2hhdCB0aG9zZSBkbywgd2UgY2FuIGlnbm9yZSANCnRob3NlIGZvciB0aGUgdGltZSBiZWluZy4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgemVyb2VzIGluIHRoZSBgc2VsbF9wcmljZWAgDQphbmQgYGJ1eV9wcmljZWAgY29sdW1ucy4gVGhvc2UgYXJlIGVzc2VudGlhbGx5IE5BJ3MgYXMgYSBgc2VsbF9wcmljZWAgb2YgMCBtZWFucyANCnRoYXQgeW91IGNhbid0IHNlbGwgdGhlIHByb2R1Y3QgdGhlcmUgYW5kIGEgYGJ1eV9wcmljZWAgb2YgMCBtZWFucyB0aGVyZSBhcmUgDQpub25lIGF2YWlsYWJsZSBhdCB0aGF0IHN0YXRpb24uDQoNCmBgYHtyfQ0KbGlzdGluZ3MgJT4lIA0KICBmaWx0ZXIoY29tbW9kaXR5X2lkID09IDEpICU+JSANCiAgc2VsZWN0KGJ1eV9wcmljZSwgc2VsbF9wcmljZSkgJT4lIA0KICBnYXRoZXIodHlwZSwgcHJpY2UpICU+JSANCiAgZmlsdGVyKHByaWNlID4gMCkgJT4lIA0KICBncm91cF9ieSh0eXBlKSAlPiUgDQogIG11dGF0ZShhdmVyYWdlID0gbWVhbihwcmljZSksIG1lZGlhbiA9IG1lZGlhbihwcmljZSkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gcHJpY2UsIGZpbGwgPSB0eXBlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArDQogIGZhY2V0X2dyaWQodHlwZSB+LikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gYXZlcmFnZSkpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbiksIGxpbmV0eXBlID0gImRvdHRlZCIpDQpgYGANCg0KVGhpcyBmaWd1cmUgZ2l2ZXMgYSBxdWljayBoaXN0b2dyYW0gb2YgdGhlIGJ1eSBhbmQgc2VsbCBwcmljZXMgb2YgYSBzaW5nbGUgDQpjb21tb2RpdHkgYWNyb3NzIGFsbCBhdmFpbGFibGUgc3RhdGlvbnMuIE5vdGUgdGhhdCBpdCBmaWx0ZXJzIG91dCBhbnkgYnV5IG9yIA0Kc2VsbCBwcmljZXMgb2YgMC4gVGhlIHNvbGlkIGxpbmUgaXMgdGhlIG1lYW4gb2YgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgdGhlIGRvdHRlZCANCmxpbmUgaXMgdGhlIG1lZGlhbiBvZiB0aGUgZGlzdHJpYnV0aW9uLiBGcm9tIHRoZSBmaWd1cmUsIGFuIGludGVyZXN0aW5nIHRvcGljIA0KdG8gaW52ZXN0aWdhdGUgaXMgbG9va2luZyBhdCBjb21wYXJhdGl2ZSBidXkgYW5kIHNlbGwgcHJpY2VzIGFjcm9zcyBzdGF0aW9ucyANCnRvIGNyZWF0ZSBhIHN5c3RlbSB0byBzZWFyY2ggZm9yIHRoZSBtYXggZGlmZmVyZW5jZS4NCg0KYGBge3J9DQpsaXN0aW5ncyAlPiUgDQogIGZpbHRlcihzdGF0aW9uX2lkID09IDEyKSAlPiUgDQogIHNlbGVjdChidXlfcHJpY2UsIHNlbGxfcHJpY2UpICU+JSANCiAgZ2F0aGVyKHR5cGUsIHByaWNlKSAlPiUgDQogIGZpbHRlcihwcmljZSA+IDApICU+JSANCiAgZ3JvdXBfYnkodHlwZSkgJT4lIA0KICBtdXRhdGUoYXZlcmFnZSA9IG1lYW4ocHJpY2UpLCBtZWRpYW4gPSBtZWRpYW4ocHJpY2UpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHByaWNlLCBmaWxsID0gdHlwZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwKSArDQogIGZhY2V0X2dyaWQodHlwZSB+LikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gYXZlcmFnZSkpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbiksIGxpbmV0eXBlID0gImRvdHRlZCIpDQpgYGANCg0KVGhpcyBmaWd1cmUgbG9va3MgYXQgYSBoaXN0b2dyYW0gb2YgYnV5IGFuZCBzZWxsIHByaWNlcyBmb3IgYSBzcGVjaWZpYyBzdGF0aW9uIA0KKGFjcm9zcyBhbGwgYXZhaWxhYmxlIGNvbW1vZGl0aWVzKS4gQWdhaW4sIHRoZSBzb2xpZCBsaW5lIGlzIHRoZSBtZWFuIGFuZCB0aGUgDQpkb3R0ZWQgbGluZSBpcyB0aGUgbWVkaWFuLg0KDQojIyMgUXVlc3Rpb25zIA0KDQpJIHdhbnQgdG8gcHV0IHRvZ2V0aGVyIHNvbWUgaW5pdGlhbCBxdWVzdGlvbnMgdG8gYW5zd2VyIHVzaW5nIG15IFNoaW55IGFwcC4gDQoNCiogV2hhdCBpcyB0aGUgYmlnZ2VzdCBwcmljZSBkaWZmZXJlbmNlPw0KKiBXaGF0IGlzIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBzdGF0aW9ucyB3aXRoIHRoZSBiaWdnZXN0IHByaWNlIGRpZmZlcmVuY2U/IA0KKiBHaXZlbiBhIHBhcnRpY3VsYXIgY29tbW9kaXR5LCB3aGVyZSBjYW4gSSBmaW5kIGl0IGFuZCBhdCB3aGF0IHByaWNlPw0KKiBHaXZlbiBhIHBhcnRpY3VsYXIgY29tbW9kaXR5LCB3aGVyZSBjYW4gSSBzZWxsIGl0IGFuZCBhdCB3aGF0IHByaWNlPw0KKiBIb3cgZG8gdGhlIGJ1eWluZyBhbmQgc2VsbGluZyBwcmljZSBjb21wYXJlIHRoZSB0aGUgZ2FsYXRpYyBhdmVyYWdlIChhbmQgDQpyZXBvcnRlZCBhdmVyYWdlIGF2ZXJhZ2UgYW5kIG1lZGlhbik/DQoNCiMjIFN0YXRpb25zIA0KDQpgYGB7cn0NCnN0YXRpb25zIDwtIGFzX3RpYmJsZShmcm9tSlNPTigiZGF0YS9zdGF0aW9ucy5qc29uIikpDQpgYGANCg0KYGBge3J9DQpoZWFkKHN0YXRpb25zKQ0KYGBgDQoNClRoZSBzdGF0aW9ucyBkYXRhIGhhcyAzOSBjb2x1bW5zIGluIGl0LCB0aGUgbW9zdCByZWxldmFudCBvZiB3aGljaCBhcmUgdGhlIA0KZm9sbG93aW5nOg0KDQoqIGlkOiBUaGUgc3RhdGlvbidzIGlkDQoqIG5hbWU6IFRoZSBzdGF0aW9uJ3MgbmFtZQ0KKiBzeXN0ZW1faWQ6IFRoZSBzeXN0ZW0gaWQgd2hlcmUgdGhlIHN0YXRpb24gcmVzaWRlcw0KKiB1cGRhdGVkX2F0OiBUaW1lIGF0IHdoaWNoIHRoZSBkYXRhIHdhcyB1cGRhdGVkDQoqIGRpc3RhbmNlX3RvX3N0YXI6IERpc3RhbmNlIG9mIHRoZSBzdGF0aW9uIGZyb20gdGhlIHN5c3RlbSdzIHN0YXINCiogaGFzXyA6IEEgc2V0IG9mIGJvb2xlYW5zIHNob3dpbmcgd2hhdCBhbWVuaXRpZXMgdGhlIHN0YXRpb24gb2ZmZXJzDQoqIGlzX3BsYW5ldGFyeTogQSBib29sZWFuIHNob3dpbmcgd2hldGhlciB0aGUgc3RhdGlvbiBpcyBvbiBhIHBsYW5ldA0KKiBzZWxsaW5nX3NoaXBzOiBBIGxpc3Qgb2YgdGhlIHNoaXBzIGJlaW5nIHNvbGQNCg0KVGhlcmUgYXJlIGFsc28gYSBudW1iZXIgb2Ygb3RoZXIgdXNlZnVsIHZhcmlhYmxlcyBzaG93aW5nIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gDQphYm91dCB0aGUgc3RhdGlvbnMuDQoNCldlIGNhbiBzdW1tYXJpemUgdGhlIG1vc3QgcmVsZXZhbnQgY29sdW1ucyBhbmQgZ2V0IGEgc2Vuc2Ugb2YgZGlzdHJpYnV0aW9ucyBhbmQgDQphbnkgcG9zc2libGUgTkFzLg0KDQpgYGB7cn0NCnN0YXRpb25zICU+JSANCiAgc2VsZWN0KGlkLCBzeXN0ZW1faWQsIHVwZGF0ZWRfYXQsIGRpc3RhbmNlX3RvX3N0YXIsIHN0YXJ0c193aXRoKCJoYXNfIiksIGlzX3BsYW5ldGFyeSkgJT4lIA0KICBzdW1tYXJ5KCkNCmBgYA0KDQpXZSBjYW4gYWxzbyBleHRyYWN0IGEgbGlzdCBvZiBwb3NzaWJsZSBzaGlwcyB0aGF0IGFyZSBzb2xkIGFuZCBmcm9tIHRoYXQgd2UgDQpjYW4gZGV0ZXJtaW5lIHdoYXQgc3RhdGlvbnMgc2VsbCB3aGljaCBzaGlwcy4NCg0KYGBge3J9DQpzdGF0aW9ucyAlPiUgDQogIHNlbGVjdChzZWxsaW5nX3NoaXBzKSAlPiUgDQogIHVubmVzdCgpICU+JSANCiAgZGlzdGluY3QoKQ0KYGBgDQoNCmBgYHtyfQ0Kc3RhdGlvbnMgJT4lIA0KICBzZWxlY3QoaWQsIG5hbWUsIHN5c3RlbV9pZCwgc2VsbGluZ19zaGlwcykgJT4lIA0KICB1bm5lc3QoKSAlPiUgDQogIGZpbHRlcihzZWxsaW5nX3NoaXBzID09ICJJbXBlcmlhbCBDdXR0ZXIiKSAlPiUgDQogIGhlYWQoKQ0KYGBgDQoNCkZvciBpbnN0YW5jZSwgd2UgY2FuIGdldCBhIGxpc3Rpbmcgb2YgYWxsIG9mIHRoZSBzdGF0aW9ucyB0aGF0IHNlbGwgdGhlIA0KIkltcGVyaWFsIEN1dHRlciIgYW5kIHdoYXQgc3lzdGVtIHRoYXQgc3RhdGlvbiBpcyBpbi4gDQoNCiMjIyBRdWVzdGlvbnMgDQoNCiogV2hhdCBhcmUgdGhlIGxpc3Qgb2YgcHVyY2hhc2FibGUgc2hpcHM/DQoqIFdoYXQgc2hpcCBjYW4gYmUgYm91Z2h0IHdoZXJlPw0KKiBXaGF0IG1vZHVsZSBjYW4gYmUgYm91Z2h0IHdoZXJlPw0KDQojIyBQb3B1bGF0ZWQgU3lzdGVtcw0KDQpUaGUgcG9wdWxhdGVkIHN5c3RlbXMgZGF0YSBnaXZlcyBpbmZvcm1hdGlvbiBvbiBzeXN0ZW1zIGluIHRoZSB1bml2ZXJzZSB0aGF0IA0KYXJlIHBvcHVsYXRlZC4NCg0KYGBge3J9DQpwb3B1bGF0ZWRfc3lzdGVtcyA8LSBhc190aWJibGUoZnJvbUpTT04oImRhdGEvc3lzdGVtc19wb3B1bGF0ZWQuanNvbiIpKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChwb3B1bGF0ZWRfc3lzdGVtcykNCmBgYA0KDQpMaWtlIHRoZSBzdGF0aW9ucyBkYXRhLCB0aGUgcG9wdWxhdGVkIHN5c3RlbXMgZGF0YSBoYXMgYSBsYXJnZSBudW1iZXIgb2YgDQpjb2x1bW5zICgyOSkgdGhhdCBhcmUgbW9zdGx5IG1ldGFkYXRhIGFib3V0IHRoZSBzeXN0ZW1zLiBNb3N0IG9mIHRoZSBtZXRhZGF0YSANCmlzIGNlbnRlcmVkIG9uIGdvdmVybm1lbnQgdHlwZXMsIGFsbGVpZ2FuY2UsIHNlY3VyaXR5IGxldmVsLCBwb3dlciBwbGF5IGluZm8sIA0KYW5kIGZhY3Rpb25zLiBUaGUgaW5mb3JtYXRpb24gdGhhdCB3aWxsIGxpa2VseSBiZSB1c2VmdWwgZm9yIHVzIGFyZSB0aGUgDQpmb2xsb3dpbmcgY29sdW1uczogDQoNCiogaWQ6IFRoZSBzeXN0ZW0ncyBpZA0KKiBuYW1lOiB0aGUgc3lzdGVtJ3MgbmFtZQ0KKiB4LCB5LCB6OiBUaGUgY29vcmRpbmF0ZXMgb2YgdGhlIHN5c3RlbSBpbiB0aGUgdW5pdmVyc2UNCg0KRmlyc3QsIEkgd2FudCB0byBsb29rIGF0IHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBjb29yZGluYXRlcyB0byB2ZXJpZnkgDQp0aGF0IHRoZXJlIGFyZSBubyBtaXNzaW5nIGNvb3JkaW5hdGVzLiBJdCBsb29rcyBsaWtlIGFsbCBvZiB0aGUgY29vcmRpbmF0ZXMgDQphcmUgcHJlc2VudC4NCg0KYGBge3J9DQpwb3B1bGF0ZWRfc3lzdGVtcyAlPiUgDQogIHNlbGVjdCh4LCB5LCB6KSAlPiUgDQogIHN1bW1hcnkoKQ0KYGBgDQoNCkkgY2FuIG1ha2UgYSAzRCBzY2F0dGVyIHBsb3Qgb2YgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBzeXN0ZW1zIGFuZCBzaG93IHRoZSANCmNvbG9yIG9mIHRoZSBjb250cm9sbGluZyBwb3dlci4gQWxsIG9mIHRoaXMgZG9uZSB3aXRoIHBsb3RseSBhbmQgY291bGQgYmUgYSBmdW4gDQp3YXkgb2Ygc2hvd2luZyB3aGVyZSBhIHBhcnRpY3VsYXIgc3lzdGVtIGlzIGluIGNvbXBhcmlzb24gdG8gb3RoZXIgc3lzdGVtcy4gSW4gDQp0aGlzIHBsb3QgSSd2ZSBvbmx5IGluY2x1ZGVkIHRoZSB0b3AgNCBjb250cm9sbGluZyBwb3dlcnMgKGJhc2VkIG9uIG51bWJlciBvZiANCnN5c3RlbXMgY29udHJvbGxlZCkuIFNpbmNlIEkgaGF2ZW4ndCB1c2VkIHBsb3RseSBtdWNoLCB0aGlzIGNvdWxkIGJlIGEgZ3JlYXQgDQpvcHBvcnR1bml0eSB0byBiZWNvbWUgbW9yZSBmYW1pbGlhciB3aXRoIGl0Lg0KDQpgYGB7cn0NCnAgPC0gcG9wdWxhdGVkX3N5c3RlbXMgJT4lDQogIGZpbHRlcighaXMubmEocG93ZXIpKSAlPiUgDQogIGdyb3VwX2J5KHBvd2VyKSAlPiUNCiAgbXV0YXRlKGNvbnRyb2xfbnVtYmVyID0gbigpKSAlPiUNCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGNvbnRyb2xfbnVtYmVyID0gZGVuc2VfcmFuayhkZXNjKGNvbnRyb2xfbnVtYmVyKSkpICU+JSANCiAgZmlsdGVyKGNvbnRyb2xfbnVtYmVyIDwgNSkgJT4lDQogIHBsb3RfbHkoeCA9IH54LCB5ID0gfnksIHogPSB+eiwgY29sb3IgPSB+cG93ZXIpICU+JSANCiAgYWRkX21hcmtlcnMob3BhY2l0eSA9IDAuMSkNCg0KcA0KYGBgDQoNCiMjIyBRdWVzdGlvbnMgDQoNCiogV2hlcmUgYXJlIHRoZSB2YXJpb3VzIHN5c3RlbXM/DQoqIFdoYXQgcG93ZXJzIG9yIGZhY3Rpb25zIGNvbnRyb2wgZWFjaCBzeXN0ZW0/DQoNCiMjIEZhY3Rpb25zDQoNClRoZSBmYWN0aW9ucyBkYXRhIHNldCBnaXZlcyBtZXRhZGF0YSBvbiBhbGwgb2YgdGhlIGZhY3Rpb25zIGludm9sdmVkIGluIHRoZSANCmdhbWUuDQoNCmBgYHtyfQ0KZmFjdGlvbnMgPC0gYXNfdGliYmxlKGZyb21KU09OKCJkYXRhL2ZhY3Rpb25zLmpzb24iKSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoZmFjdGlvbnMpDQpgYGANCg0KSSBhbSB1bmxpa2VseSB0byB1c2UgdGhpcyBkYXRhIHNldCBmb3IgdGhpcyBwYXJ0aWN1bGFyIHByb2plY3QsIGJ1dCBpdCBjb3VsZCANCnByb3ZpZGUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBpZiBJIHdhbnQgdG8gbG9vayBhdCBzcGVjaWZpYyBmYWN0aW9ucywgDQpwYXJ0aWN1bGFybHkgcGxheWVyIGNyZWF0ZWQgZmFjdGlvbnMuDQoNCg==